// Copyright (c) Microsoft Corporation. 
// Licensed under the MIT license.

#include "NavSysVisibilityDataVisualizer.h"
#include "CoreMinimal.h"
#include "LevelEditor.h"
#include "Editor.h"
#include "UObject/ConstructorHelpers.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "Algo/AnyOf.h"
#include "UObject/UObjectBaseUtility.h"
#include "UObject/ObjectMacros.h"
#include "ISimplygonReduction.h"
#include "SimplygonPluginMeshReductionModule.h"
#include "SimplygonEditorModule.h"

ASimplygonVisibilitySamplerVisualizer::ASimplygonVisibilitySamplerVisualizer(const FObjectInitializer& InInitializer)
: AActor(InInitializer)
{
#if WITH_EDITORONLY_DATA
	Component = CreateDefaultSubobject<UInstancedStaticMeshComponent>(TEXT("InstancedStaticMeshComponent0"));
	RootComponent = Component;
	Component->Mobility = EComponentMobility::Static;
	Component->SetGenerateOverlapEvents(false);
	Component->CastShadow = false;
	Component->bCastStaticShadow = false;
	Component->bCastDynamicShadow = false;
	Component->bAllowCullDistanceVolume = false;

	SpriteComponent = CreateEditorOnlyDefaultSubobject<UBillboardComponent>(TEXT("Sprite"));
	if (!IsRunningCommandlet() && (SpriteComponent != nullptr))
	{
		// Structure to hold one-time initialization
		struct FConstructorStatics
		{
			ConstructorHelpers::FObjectFinderOptional<UTexture2D> SpriteTexture;
			FName ID_Info;
			FText NAME_Info;
			FConstructorStatics()
				: SpriteTexture(TEXT("/Simplygon/Textures/SimplygonVisibility"))
				, ID_Info(TEXT("SimplygonVisibilityVisualizer"))
				, NAME_Info(NSLOCTEXT("SpriteCategory", "SimplygonVisibilityVisualizer", "SimplygonVisibilityVisualizer"))
			{
			}
		};
		static FConstructorStatics ConstructorStatics;

		SpriteComponent->Sprite = ConstructorStatics.SpriteTexture.Get();
		SpriteComponent->SetRelativeScale3D(FVector(0.5f, 0.5f, 0.5f));
		SpriteComponent->SpriteInfo.Category = ConstructorStatics.ID_Info;
		SpriteComponent->SpriteInfo.DisplayName = ConstructorStatics.NAME_Info;
		SpriteComponent->bIsScreenSizeScaled = true;
		SpriteComponent->bHiddenInGame = true;
		SpriteComponent->SetupAttachment(Component);
		SpriteComponent->bIsScreenSizeScaled = true;

	}
	//bLockLocation = true;
	bIsEditorOnlyActor = true;
	SetActorEnableCollision(false);
	ConstructorHelpers::FObjectFinder<UStaticMesh> MeshSphereAsset(TEXT("/Engine/BasicShapes/Sphere.Sphere"));
	ConstructorHelpers::FObjectFinder<UMaterialInstance> MaterialAsset(TEXT("/Simplygon/Materials/MI_SG_Visualizer.MI_SG_Visualizer"));

	MinZ = 45.0f;
	MaxZ = 180.0f;
	NumOfSamples = 100;

	if (MeshSphereAsset.Succeeded())
	{
		Component->SetStaticMesh(MeshSphereAsset.Object);
		Component->bIsEditorOnly = true;
	}

	if (MaterialAsset.Succeeded())
	{
		Component->SetMaterial(0, MaterialAsset.Object);
	}
#endif
}

void ASimplygonVisibilitySamplerVisualizer::ClearSamples()
{
#if WITH_EDITORONLY_DATA
	SampleLocations.Empty();
#endif
}

#if WITH_EDITOR
void ASimplygonVisibilitySamplerVisualizer::PostLoad()
{

	UpdateMeshReductionSamples();
	AActor::PostLoad();
}

void ASimplygonVisibilitySamplerVisualizer::PostEditMove(bool bFinished)
{
	UpdateInstances();
	AActor::PostEditMove(bFinished);
}

#endif

void ASimplygonVisibilitySamplerVisualizer::UpdateInstances()
{
#if WITH_EDITORONLY_DATA
	FVector UniformScale(0.1f, 0.1f, 0.1f);

	Component->ClearInstances();

	for(const auto& Location: SampleLocations)
	{
		Component->AddInstanceWorldSpace(FTransform(FRotator::ZeroRotator, Location, UniformScale));
	}
#endif
}


void ASimplygonVisibilitySamplerVisualizer::ToggleInEditor()
{
#if WITH_EDITORONLY_DATA
	if (Component->GetInstanceCount() == 0 && SampleLocations.Num() > 0)
		Build();

	Component->ToggleVisibility(true);
#endif
}

void ASimplygonVisibilitySamplerVisualizer::Build()
{
	
	ISimplygonEditorModule& SimplygonEditorModule = FModuleManager::LoadModuleChecked<ISimplygonEditorModule>("SimplygonEditor");
#if WITH_EDITORONLY_DATA
	SampleLocations.Empty();
	SimplygonEditorModule.GetRandomSamplesFromNavigableSpace(this->GetWorld(), NumOfSamples, MinZ, MaxZ, SampleLocations);

	UpdateInstances();
	UpdateMeshReductionSamples();
#endif
}

#if WITH_EDITORONLY_DATA
/** Returns SpriteComponent subobject **/
UBillboardComponent* ASimplygonVisibilitySamplerVisualizer::GetSpriteComponent() const { return SpriteComponent; }
#endif

void ASimplygonVisibilitySamplerVisualizer::UpdateMeshReductionSamples()
{
#if WITH_EDITORONLY_DATA
	TArray<FVector> TransformedSamplePoints(SampleLocations);
	int32 NumInstances = Component->GetInstanceCount();
	for (int32 Index = 0; Index < NumInstances; ++Index)
	{
		FTransform InstanceTransform = FTransform::Identity;
		Component->GetInstanceTransform(Index, InstanceTransform, true);
		TransformedSamplePoints[Index] = InstanceTransform.GetTranslation();
	}
	ISimplygonPluginMeshReductionModule& SimplygonModule = FModuleManager::GetModuleChecked<ISimplygonPluginMeshReductionModule>("SimplygonPluginMeshReduction");
	SimplygonModule.GetSimplygonReduction()->GetNavmeshVisibilitySamplerUpdateHandler().ExecuteIfBound(TransformedSamplePoints);
	SimplygonModule.GetDistributedMeshMerging()->GetNavmeshVisibilitySamplerUpdateHandler().ExecuteIfBound(TransformedSamplePoints);
#endif
}
